﻿using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using DG.Tweening;

public class DrawRealize2DForDotween : MonoSingleton<DrawRealize2DForDotween>
{

    // 数字UI的父对象
    private Transform m_Number;
    // 刷子
    private Transform m_Brush;
    // 显示层
    private Transform m_DrawMask;
    // 跟随鼠标的标记点
    private Image m_Sign;
    // 
    private Canvas m_Canvas;
    // 完成事件回调
    private System.Action completeCallback = null;


    // 整条动画的运动时间
    private readonly float m_MoveDuration = 1.3f;
    // 整条动画的运动时间
    private readonly float m_MoveCheckDuration = 150f;
    // 路劲索引是否增加的判断距离
    private readonly float m_MinDis = 10.0f;
    // 新的点跟上一个点的最大间隔距离
    private readonly float m_NewPointMaxDis = 50;
    // 画的时候，左右最大偏移量
    private readonly float m_MaxProjectDis = 30;
    // 多长距离创建一个面片
    private readonly float m_DrawMaskDis = 1f;

    // 当前路径索引
    [SerializeField]
    private int m_WayPointIndex = 0;
    // 当前写的笔画步骤
    [SerializeField]
    private int m_DrawOrder = 0;

    // 是否正在画
    private bool bIsDrawing = false;

    private bool bIsHoving = false;

    private bool bIsFirstCreateMask;

    // ui的材质球
    private Material m_NumberMaterial;

    // 当前画刷要移动的位置
    private Vector3 m_CurrentPoint;
    // 画刷上次记录位置
    private Vector3 m_PreviousPoint;

    // 当前画刷要移动的位置
    private Transform m_CurrentPointSign;

    // 数字笔画Image的一个数组
    [SerializeField]
    private Transform[] m_NumberArray;
    // 当前笔画的路径数组
    [SerializeField]
    private Vector3[] m_WayPoints;

    List<GameObject> m_MaskList = new List<GameObject>();
    List<GameObject> m_DrawList = new List<GameObject>();

    // 笔画的路径数组
    private Dictionary<int, Vector3[]> drawPathDict = new Dictionary<int, Vector3[]>();

    private const string downShader = "Custom/UnlitStencilDown";
    private const string upShader = "Custom/UnlitStencilUp";
    
    void Update()
    {
        if (Input.GetMouseButtonDown(0))
            OnClickDown();
        if (Input.GetMouseButton(0))
            OnClickHover();
        if (Input.GetMouseButtonUp(0))
            OnClickUp();

        m_Sign.transform.position = UGUITool.ScreenPointToUGUIPoint(m_Canvas, Input.mousePosition);
    }

    public void DrawNumber(Transform number, Transform brush, Transform mask, Image sign, Canvas canvas, System.Action callback = null)
    {
        Clear();

        m_Number = number;
        m_Brush = brush;
        m_DrawMask = mask;
        m_Sign = sign;
        m_Canvas = canvas;
        completeCallback = callback;

        Initialize();
        StartDraw();
    }

    private void Initialize()
    {
        m_Number.gameObject.SetActive(true);
        m_Brush.gameObject.SetActive(true);
        m_Sign.gameObject.SetActive(false);
        //m_Sign.gameObject.SetActive(true);
        m_CurrentPointSign = GameObject.Instantiate(m_Sign.transform);
        m_CurrentPointSign.SetParent(m_Sign.transform.parent);
        m_CurrentPointSign.localScale = Vector3.one * 0.5f;
        //m_CurrentPointSign.gameObject.SetActive(false);
        m_CurrentPointSign.gameObject.SetActive(true);
        bIsFirstCreateMask = true;
        bIsDrawing = false;
        bIsHoving = false;

        m_WayPointIndex = 0;
        m_DrawOrder = 0;

        SetUpMaterial(m_DrawMask);

        DrawPathMono[] pathMonos = m_Number.GetComponentsInChildren<DrawPathMono>();
        for (int i = 0; i < pathMonos.Length; i++)
        {
            DrawPathMono mono = pathMonos[i];
            drawPathDict.Add(mono.pathID, mono.pathArray);
        }

        Transform Number_Order = m_Number.Find("Order");
        m_NumberArray = new Transform[Number_Order.childCount];
        for (int i = 0; i < Number_Order.childCount; i++)
        {
            m_NumberArray[i] = Number_Order.GetChild(i);
            //Debug.Log("index:" + i + "   Name:" + m_NumberArray[i].name);
        }
    }

    private void StartDraw()
    {
        if (m_NumberArray.Length <= 0)
        {
            Debug.LogError("数据错误！");
            return;
        }

        for (int i = 0; i < m_NumberArray.Length; i++)
        {
            m_NumberArray[i].gameObject.SetActive(false);
            Transform hint = m_NumberArray[i].GetChild(0);
            if (hint != null)
            {
                hint.gameObject.SetActive(false);
            }
        }

        m_DrawOrder = 0;
        DrawNumberOrder(m_DrawOrder);
    }

    private void DrawNumberOrder(int orderIndex)
    {
        Transform target = m_NumberArray[orderIndex];

        bIsHoving = false;
        bIsDrawing = true;
        m_WayPointIndex = 0;
        target.gameObject.SetActive(true);
        Transform hint = target.GetChild(0);
        if (hint != null)
        {
            hint.gameObject.SetActive(true);
        }

        SetDrawMaterial(target);
        m_WayPoints = drawPathDict[orderIndex];

        m_Brush.position = m_CurrentPoint = m_PreviousPoint = m_WayPoints[0];
        m_Brush.rotation = Quaternion.LookRotation((m_WayPoints[1] - m_WayPoints[0]).normalized, m_Brush.up);
        //Debug.LogError(m_Brush.up);

        float dis = GetPathDistance(m_WayPoints);
        Debug.LogError(dis);

        //m_Brush.DOPath(m_WayPoints, m_MoveDuration, PathType.CatmullRom, PathMode.Full3D).SetLookAt(0.001f);
        m_Brush.DOKill();
        m_Brush.DOPath(m_WayPoints, dis / m_MoveCheckDuration, PathType.CatmullRom, PathMode.Full3D).SetLookAt(0.001f);
        m_Brush.DOPause();
    }

    private void DrawComplete()
    {
        Debug.Log("全部画完！");
        bIsDrawing = false;
        if (completeCallback != null)
        {
            completeCallback();
        }
        Clear();
        DestroyInstance();
    }

    private void OnClickDown()
    {
        bIsHoving = true;
    }

    private void OnClickHover()
    {
        CheckBrushMove();
        if (!bIsDrawing) return;
        if (!bIsHoving) return;

        CheckNextMovePoint();
        CheckNextWayPointIndex();
        CheckDrawMask();
    }

    private void OnClickUp()
    {
        bIsHoving = false;

        m_Brush.DOPause();
    }

    private void DrawNextOrder()
    {
        m_Brush.DOKill();

        m_DrawOrder++;
        if (m_DrawOrder >= m_NumberArray.Length)
        {
            bIsDrawing = false;
            DrawComplete();
            return;
        }
        else
        {
            //Debug.Log("--------------: DrawNext");
            DrawNumberOrder(m_DrawOrder);
        }
    }

    private void DrawCurrentOrderEnd()
    {
        foreach (GameObject go in m_DrawList)
        {
            go.SetActive(false);
        }

        m_MaskList.AddRange(m_DrawList.ToArray());
        m_DrawList.Clear();

        SetDefaultMaterial(m_NumberArray[m_DrawOrder]);

        DrawNextOrder();
    }

    void CheckBrushMove()
    {
        float distance = Vector3.Distance(m_Brush.position, m_CurrentPoint);

        Vector3 startPoint = m_CurrentPoint;
        Vector3 endPoint = Vector3.zero;
        if (m_WayPointIndex < m_WayPoints.Length - 1)
            endPoint = m_WayPoints[m_WayPointIndex + 1];
        else
        {
            endPoint = m_WayPoints[m_WayPoints.Length - 1];
        }

        //Debug.Log("-----: " + distance);
        if (distance < 5)
        {
            m_Brush.DOPause();
            if (m_WayPointIndex >= m_WayPoints.Length - 1)
            {
                DrawCurrentOrderEnd();
            }
        }
        else if (Vector3.Distance(m_Brush.position, endPoint) < Vector3.Distance(startPoint, endPoint))
        {
            m_Brush.DOPause();
        }
        //else if (Vector3.Dot((m_CurrentPoint - m_Brush.position).normalized, (m_Brush.position - m_PreviousPoint).normalized) < 0)
        //{
        //    //Debug.Log("-----Dot: m_Brush.DOPause ");
        //    m_Brush.DOPause();
        else
        {
            m_Brush.DOPlay();
        }
    }

    void CheckNextMovePoint()
    {
        Vector3 signPosition = m_Sign.transform.position;
        if (m_WayPointIndex < m_WayPoints.Length - 1)
        {
            Vector3 startPoint = m_CurrentPoint;
            Vector3 endPoint = m_WayPoints[m_WayPointIndex + 1];

            //Debug.DrawLine(startPoint, endPoint, Color.red);

            Vector3 moveDirection = endPoint - startPoint; // 移动方向向量
            Vector3 projectDirection = signPosition - startPoint; // 投射方向向量

            //Debug.DrawLine(startPoint, signPosition, Color.red);

            if (Vector3.Dot(moveDirection.normalized, projectDirection.normalized) < 0)
                return;
            Vector3 projectPoint = Vector3.Project(projectDirection, moveDirection.normalized) + startPoint; //投射点

            //Debug.DrawLine(projectPoint, hit.point, Color.white);

            if (Vector3.Distance(projectPoint, startPoint) > moveDirection.magnitude + 1)
                return;

            float projectDis = Vector3.Distance(projectPoint, signPosition);
            Debug.Log("左右距离：" + projectDis);
            if (projectDis < m_MaxProjectDis)
            {
                Debug.Log("前面距离：" + Vector3.Distance(m_CurrentPoint, projectPoint));
                if (Vector3.Distance(m_CurrentPoint, projectPoint) < m_NewPointMaxDis)
                {
                    m_CurrentPoint = projectPoint;
                    m_CurrentPointSign.position = m_CurrentPoint;
                }
            }
        }
        else
        {
            // 到达最后一个点
            m_CurrentPoint = m_WayPoints[m_WayPoints.Length - 1];
            m_CurrentPointSign.position = m_CurrentPoint;
        }

    }

    void CheckNextWayPointIndex()
    {
        if (m_WayPointIndex >= m_WayPoints.Length - 1)
            return;
        if (Vector3.Distance(m_CurrentPoint, m_WayPoints[m_WayPointIndex + 1]) < m_MinDis)
        {
            m_WayPointIndex++;
        }
    }

    void CheckDrawMask()
    {
        if (bIsFirstCreateMask)
        {
            float dis1 = Vector3.Distance(m_Brush.position, m_PreviousPoint);
            if (dis1 > 1)
            {
                bIsFirstCreateMask = false;
                CreateMask();
            }
        }

        float dis = Vector3.Distance(m_Brush.position, m_PreviousPoint);
        if (dis > m_DrawMaskDis)
        {
            CreateMask();
            m_PreviousPoint = m_Brush.position;
        }
    }

    void CreateMask()
    {
        Transform go = GameObject.Instantiate(m_DrawMask);
        go.SetParent(m_Number);
        go.gameObject.SetActive(true);
        go.position = m_Brush.Find("BrushImage").position;
        go.localScale = Vector3.one;
        m_DrawList.Add(go.gameObject);
    }


    private void SetDrawMaterial(Transform target)
    {
        if (m_NumberMaterial == null)
        {
            m_NumberMaterial = new Material(Shader.Find(downShader));
        }
        target.GetComponent<Image>().material = m_NumberMaterial;
    }

    private void SetDefaultMaterial(Transform target)
    {
        target.GetComponent<Image>().material = null;
    }

    private void SetUpMaterial(Transform target)
    {
        Image image = target.GetComponent<Image>();
        image.material.shader = Shader.Find(upShader);
    }

    private float GetPathDistance(Vector3[] paths)
    {
        float dis = 0;
        for (int i = 1; i < paths.Length; i++)
        {
            float addDis = Vector3.Distance(paths[i - 1], paths[i]);
            dis += addDis;
        }
        return dis;
    }

    protected override void OnDestroy()
    {
        Clear();
        base.OnDestroy();
    }

    private void Clear()
    {
        foreach (GameObject go in m_MaskList)
        {
            GameObject.Destroy(go);
        }
        foreach (GameObject go in m_DrawList)
        {
            GameObject.Destroy(go);
        }
        m_MaskList.Clear();
        m_DrawList.Clear();
        drawPathDict.Clear();
        if (m_Brush != null)
        {
            m_Brush.DOKill();
            m_Brush.gameObject.SetActive(false);
        }
        if (m_Sign != null)
            m_Sign.gameObject.SetActive(false);
        if (m_CurrentPointSign != null)
        {
            Destroy(m_CurrentPointSign.gameObject);
        }
        m_CurrentPointSign = null;
        m_NumberMaterial = null;
    }


}
